﻿using TrueSync;

namespace HEFramework
{
    public static class SphereCollisionComponentSystem
    {
        [EntitySystem]
        public class SphereCollisionComponentAwakeSystem : AwakeSystem<SphereCollisionComponent, FP, TSVector, TSVector>
        {
            protected override void Awake(SphereCollisionComponent _self, FP _a, TSVector _b, TSVector _c)
            {
                _self.Radius = _a;
                _self.Pos = _b;
                _self.Center = _c;
            }
        }


        [EntitySystem]
        public class SphereCollisionComponentUpdateSystem : UpdateSystem<SphereCollisionComponent>
        {
            protected override void Update(SphereCollisionComponent _self)
            {
                //与位置组件同步
                var tc = _self.GetParentChild<TransformComponent>();
                if (tc != null)
                {
                    _self.Pos = tc.P;
                }

                using var list = _self.GetScene().GetChildList<Entity>();
                _self.RemoveNull();
                for (int i = 0; i < list.Count; i++)
                {
                    var entity = list[i];
                    if (entity == null || entity.IsDisposed)
                    {
                        continue;
                    }

                    var scc = entity.GetChild<SphereCollisionComponent>();
                    if (scc == null || scc.InstanceID == _self.InstanceID)
                    {
                        continue;
                    }

                    int value = _self.Collision(entity);

                    if (_self.IsEnterCollision(value))
                    {
                        _self.EnterCollisionEvent?.Invoke(entity);
                    }

                    if (_self.IsCollision(value))
                    {
                        _self.CollisionEvent?.Invoke(entity);
                    }

                    if (_self.IsExitCollision(value))
                    {
                        _self.ExitCollisionEvent?.Invoke(entity);
                    }
                }
            }
        }

        private static void RemoveNull(this SphereCollisionComponent _self)
        {
            using ListObject<long> list = ListObject<long>.Create();
            foreach (var key in _self.Collisions.Keys)
            {
                // 如果值为null，则将键添加到要删除的列表中
                var entity = _self.Collisions[key].Entity;
                if (entity == null)
                {
                    list.Add(key);
                }
            }

            for (int i = 0; i < list.Count; i++)
            {
                _self.Collisions.Remove(list[i]);
            }
        }

        private static int Collision(this SphereCollisionComponent _self, Entity _entity)
        {
            int result = 0;
            result |= _self.OnEnterCollision(_entity);
            result |= _self.OnCollision(_entity);
            result |= _self.OnExitCollision(_entity);
            return result;
        }

        private static bool IsEnterCollision(this SphereCollisionComponent _self, int _value)
        {
            switch (_value)
            {
                case 1:
                case 3:
                case 5:
                case 7:
                    return true;
                default:
                    return false;
            }
        }

        private static bool IsCollision(this SphereCollisionComponent _self, int _value)
        {
            switch (_value)
            {
                case 2:
                case 3:
                case 6:
                case 7:
                    return true;
                default:
                    return false;
            }
        }

        private static bool IsExitCollision(this SphereCollisionComponent _self, int _value)
        {
            switch (_value)
            {
                case 4:
                case 5:
                case 6:
                case 7:
                    return true;
                default:
                    return false;
            }
        }

        private static int OnEnterCollision(this SphereCollisionComponent _self, Entity _entity)
        {
            if (!_self.IsCollisionObject(_entity))
            {
                return 0;
            }

            //如果存在，返回
            if (_self.Collisions.ContainsKey(_entity.InstanceID))
            {
                return 0;
            }

            //如果不包含，返回
            if (!_self.IsContain(_entity))
            {
                return 0;
            }

            //将目标加入检测队列
            _self.Collisions.Add(_entity.InstanceID, _entity);

            return 1;
        }

        private static int OnCollision(this SphereCollisionComponent _self, Entity _entity)
        {
            if (!_self.IsCollisionObject(_entity))
            {
                return 0;
            }

            //如果不存在，则返回
            if (!_self.Collisions.ContainsKey(_entity.InstanceID))
            {
                return 0;
            }

            return 2;
        }

        private static int OnExitCollision(this SphereCollisionComponent _self, Entity _entity)
        {
            if (!_self.IsCollisionObject(_entity))
            {
                return 0;
            }

            //如果不存在，则返回
            if (!_self.Collisions.ContainsKey(_entity.InstanceID))
            {
                return 0;
            }

            //如果还包含，返回False
            if (_self.IsContain(_entity))
            {
                return 0;
            }

            //将目标移除检测队列
            _self.Collisions.Remove(_entity.InstanceID);

            return 4;
        }

        /// <summary>
        /// 检测（目前只检测球和球）
        /// </summary>
        /// <param name="_self"></param>
        /// <param name="_entity"></param>
        /// <returns></returns>
        private static bool IsContain(this SphereCollisionComponent _self, Entity _entity)
        {
            var scc = _entity.GetChild<SphereCollisionComponent>();
            if (scc != null)
            {
                FP radiusA = Formula.LG(_self.Radius);
                FP radiusB = Formula.LG(scc.Radius);

                TSVector pA = _self.GetPos();
                TSVector pB = scc.GetPos();

                var dif = TSVector.Distance(pA, pB) - radiusA - radiusB;

                //距离太远
                if (dif > 0)
                {
                    return false;
                }

                return true;
            }


            return false;
        }

        /// <summary>
        /// 是否是碰撞对象
        /// </summary>
        /// <param name="_self"></param>
        /// <param name="_entity"></param>
        /// <returns></returns>
        private static bool IsCollisionObject(this SphereCollisionComponent _self, Entity _entity)
        {
            var scc = _entity.GetChild<SphereCollisionComponent>();
            if (scc == null)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// 获取坐标
        /// </summary>
        /// <param name="_self"></param>
        /// <returns></returns>
        private static TSVector GetPos(this SphereCollisionComponent _self)
        {
            return _self.Pos + _self.Center;
        }
    }
}